package org.truenewx.tnxjee.webmvc.response.exception.message;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.truenewx.tnxjee.core.util.JsonUtil;
import org.truenewx.tnxjee.core.util.LogUtil;
import org.truenewx.tnxjee.service.exception.MultiException;
import org.truenewx.tnxjee.service.exception.ResolvableException;
import org.truenewx.tnxjee.service.exception.SingleException;
import org.truenewx.tnxjee.service.exception.message.SingleExceptionMessageResolver;
import org.truenewx.tnxjee.service.exception.model.ExceptionError;
import org.truenewx.tnxjee.webmvc.method.support.HandlerMethodMapping;
import org.truenewx.tnxjee.webmvc.response.RpcResponse;
import org.truenewx.tnxjee.webmvc.security.web.RestResponseResultDecider;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 默认的可解决异常消息保存器实现
 */
public class DefaultResolvableExceptionMessageSaver implements ResolvableExceptionMessageSaver {

    @Autowired
    private SingleExceptionMessageResolver resolver;
    @Autowired
    private HandlerMethodMapping handlerMethodMapping;
    @Autowired(required = false)
    private RestResponseResultDecider restResponseResultDecider;

    @Override
    public void saveMessage(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler,
            ResolvableException re) {
        List<ExceptionError> errors = buildErrors(re, request.getLocale());
        if (!errors.isEmpty()) {
            if (this.handlerMethodMapping.isRestRequest(request, handler)) {
                saveMessageToBody(request, response, errors);
            } else {
                saveMessageToView(request, response, errors);
            }
        }
    }

    protected void saveMessageToBody(HttpServletRequest request, HttpServletResponse response,
            List<ExceptionError> errors) {
        try {
            Object body;
            if (this.restResponseResultDecider != null && this.restResponseResultDecider.supports(request)) {
                body = this.restResponseResultDecider.getErrorBody(errors);
            } else {
                body = RpcResponse.error(errors);
            }
            String json = JsonUtil.toJson(body);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.getWriter().print(json);
        } catch (IOException e) {
            LogUtil.error(getClass(), e);
        }
    }

    protected void saveMessageToView(HttpServletRequest request, HttpServletResponse response,
            List<ExceptionError> errors) {
        request.setAttribute(ATTRIBUTE, errors);
    }

    private List<ExceptionError> buildErrors(ResolvableException re, Locale locale) {
        List<ExceptionError> errors = new ArrayList<>();
        if (re instanceof SingleException) {
            errors.add(buildError((SingleException) re, locale));
        } else if (re instanceof MultiException) {
            for (SingleException se : (MultiException) re) {
                errors.add(buildError(se, locale));
            }
        }
        return errors;
    }

    private ExceptionError buildError(SingleException se, Locale locale) {
        String message = this.resolver.resolveMessage(se, locale);
        return new ExceptionError(se, message);
    }

}
